Making API Requests Using Fetch API in React Native
Course: React Native Training
Topic: Making API Requests Using Fetch API
Course Link: React Native Training at JustAcademy
Demo Link: Register for a Course Demo
JustAcademy Website: Visit JustAcademy
1. Introduction to API Requests in React Native
Modern mobile applications frequently communicate with remote servers to retrieve, create, update, and delete information. For example, an e-commerce application retrieves products, a weather application displays weather information, and a social media application loads posts and user profiles from a backend server.
An API, or Application Programming Interface, provides a structured way for different software systems to communicate. In React Native, an application can send HTTP requests to a backend API and process the response returned by the server.
React Native provides the Fetch API for network requests. Fetch uses JavaScript Promises and works with asynchronous programming techniques such as async and await.
Official documentation: React Native Networking.
1.1 What Is the Fetch API?
The Fetch API is a JavaScript interface used to request resources from a server. It allows an application to send HTTP requests and receive responses containing data or status information.
The basic syntax is:
fetch(url, options);
- URL: The address of the API endpoint.
- Options: An optional object containing the HTTP method, headers, body, and other request settings.
- Promise: Fetch returns a Promise that resolves to a Response object when the server responds.
- Response body: The returned data can be read as JSON, text, or another supported format.
1.2 Why Use Fetch API?
- It is built into React Native, so a separate HTTP client is not required for basic requests.
- It supports common HTTP methods such as GET, POST, PUT, PATCH, and DELETE.
- It supports request headers and request bodies.
- It works with Promises and async/await.
- It can retrieve data from REST APIs.
- It integrates with React hooks and component state.
- It supports request cancellation in compatible runtime environments.
2. Understanding API Communication
API communication involves sending a request from the mobile application to a server. The server processes the request and returns a response. The response usually contains an HTTP status code, headers, and optionally a body.
2.1 API Request Lifecycle
- The user opens a screen or performs an action.
- The application prepares the endpoint URL and request options.
- The Fetch API sends the HTTP request.
- The server receives and processes the request.
- The server returns an HTTP response.
- The application checks the response status.
- The application reads and parses the response body.
- React state is updated with the retrieved information.
- The interface displays the updated information to the user.
2.2 API Communication Flow
React Native Application
|
| HTTP Request
v
API Endpoint
|
v
Backend Server
|
v
Database / Services
|
| HTTP Response
v
API Endpoint
|
v
React Native Application
|
v
Update UI
2.3 Example JSON Response
A user API might return the following JSON object:
{
"id": 1,
"name": "Rahul Sharma",
"email": "[email protected]",
"username": "rahul123"
}
After parsing the response, the application can access individual properties such as user.name and user.email.
3. Understanding REST APIs and Endpoints
A REST API exposes resources through URLs called endpoints. Each endpoint represents a resource or operation that the application can access.
3.1 What Is an API Endpoint?
An endpoint is the URL used to access a particular API resource. For example, the following testing endpoint returns a collection of sample users:
https://jsonplaceholder.typicode.com/users
The endpoint is provided by JSONPlaceholder, a service commonly used for learning and testing API requests.
3.2 Common HTTP Methods
| Method |
Purpose |
Example |
| GET |
Retrieve data. |
Get a list of users. |
| POST |
Submit data or create a resource. |
Create a post. |
| PUT |
Replace an existing resource. |
Replace a complete record. |
| PATCH |
Update selected fields. |
Change a user's name. |
| DELETE |
Request removal of a resource. |
Delete a post. |
The precise behavior of each method depends on the API's implementation and documentation.
4. Making a Basic GET Request
A GET request is used to retrieve information from an API. It is one of the most common network operations in a React Native application.
4.1 Basic Fetch Example
fetch('https://jsonplaceholder.typicode.com/users')
.then(response => response.json())
.then(data => {
console.log(data);
})
.catch(error => {
console.error('Request failed:', error);
});
4.2 Explanation
fetch(url) sends a request to the endpoint.
.then() handles the next step when a Promise resolves.
response.json() reads the response body and parses it as JSON.
console.log(data) displays the parsed data for development and debugging.
.catch() handles rejected Promises and errors in the Promise chain.
4.3 Checking HTTP Response Status
Fetch does not automatically reject its Promise for HTTP error responses such as 404 Not Found or 500 Internal Server Error. The request can resolve with a Response object even when the server reports an unsuccessful status. Therefore, check response.ok or response.status.
const fetchUsers = async () => {
try {
const response = await fetch(
'https://jsonplaceholder.typicode.com/users'
);
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
const users = await response.json();
console.log(users);
} catch (error) {
console.error('Unable to fetch users:', error.message);
}
};
fetchUsers();
5. Using Async and Await with Fetch API
Async/await provides a readable way to work with asynchronous operations. It allows a developer to write sequential-looking code while waiting for network requests to complete.
5.1 Understanding Async/Await
async declares a function that returns a Promise.
await pauses the current async function until the awaited Promise settles.
try contains the operations that may fail.
catch handles errors thrown inside the try block.
finally can perform cleanup whether the operation succeeds or fails.
5.2 Example Using Async/Await
const getUser = async () => {
try {
const response = await fetch(
'https://jsonplaceholder.typicode.com/users/1'
);
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
const user = await response.json();
console.log('Name:', user.name);
console.log('Email:', user.email);
} catch (error) {
console.error('API request failed:', error.message);
}
};
getUser();
5.3 Promise Chaining vs Async/Await
| Promise Chaining |
Async/Await |
Uses .then() and .catch(). |
Uses async, await, and often try...catch. |
| Works well for short Promise chains. |
Often easier to read when multiple asynchronous steps depend on each other. |
| Each step is represented by a Promise callback. |
Steps are written in a sequential style. |
| Suitable for API requests. |
Also suitable for API requests. |
6. Displaying API Data in a React Native Component
After retrieving API data, a mobile application usually needs to display it on the screen. React's useState hook can store the data, while useEffect can initiate a request when a component mounts or when its dependencies change.
6.1 Complete Example: Users Screen
import React, { useEffect, useState } from 'react';
import {
ActivityIndicator,
FlatList,
SafeAreaView,
Text,
View
} from 'react-native';
export default function UsersScreen() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState('');
useEffect(() => {
const loadUsers = async () => {
try {
const response = await fetch(
'https://jsonplaceholder.typicode.com/users'
);
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
const data = await response.json();
setUsers(data);
} catch (err) {
setError(err.message || 'Unable to load users.');
} finally {
setLoading(false);
}
};
loadUsers();
}, []);
if (loading) {
return (
<View>
<ActivityIndicator size="large" />
<Text>Loading users...</Text>
</View>
);
}
if (error) {
return <Text>Error: {error}</Text>;
}
if (users.length === 0) {
return <Text>No users found.</Text>;
}
return (
<SafeAreaView>
<FlatList
data={users}
keyExtractor={item => String(item.id)}
renderItem={({ item }) => (
<View>
<Text>{item.name}</Text>
<Text>{item.email}</Text>
</View>
)}
/>
</SafeAreaView>
);
}
6.2 Explanation of the Component
useState([]) stores the list of users.
useState(true) marks the initial loading state.
useState('') stores an error message when a request fails.
useEffect() starts the request after the component mounts.
loadUsers() performs the asynchronous API request.
setUsers(data) updates the component with the returned records.
ActivityIndicator shows progress while the request is running.
FlatList efficiently renders a scrollable list.
keyExtractor assigns a stable key to each user.
finally ensures the loading flag is cleared after success or failure.
This example provides basic loading, success, empty, and error states. A production application may also include retry controls, pull-to-refresh, request cancellation, and more detailed error handling.
7. Handling Loading, Success, Empty, and Error States
Network requests take time and can fail for many reasons. A reliable mobile application should communicate the current request state clearly to the user.
7.1 Loading State
The loading state indicates that the application is waiting for a response. A spinner or loading message can prevent the interface from appearing unresponsive.
const [loading, setLoading] = useState(false);
const loadData = async () => {
setLoading(true);
try {
// Perform the API request.
} finally {
setLoading(false);
}
};
7.2 Success State
When the request succeeds, parse the response and save the result in state.
const data = await response.json();
setUsers(data);
7.3 Empty State
An API request may succeed but return an empty array. Display an appropriate message instead of leaving the screen blank.
if (!loading && !error && users.length === 0) {
return <Text>No users found.</Text>;
}
7.4 Error State
When a request fails, display a clear message and provide a retry action where appropriate.
if (error) {
return <Text>Unable to load data: {error}</Text>;
}
7.5 Recommended UI States
| State |
Expected UI |
Possible Cause |
| Loading |
Spinner or progress message. |
Request is in progress. |
| Success |
Display retrieved information. |
Request completed successfully. |
| Empty |
Display a no-results message. |
The response contains no records. |
| Error |
Display an error and possible retry action. |
Network or HTTP failure. |
8. Making POST Requests Using Fetch API
A POST request is commonly used to submit data or create a resource. For a JSON API, the application generally serializes the request object with JSON.stringify() and sets the content type.
8.1 POST Request Example
const createPost = async () => {
const postData = {
title: 'Learning React Native',
body: 'Practicing API requests with Fetch.',
userId: 1
};
try {
const response = await fetch(
'https://jsonplaceholder.typicode.com/posts',
{
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(postData)
}
);
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
const result = await response.json();
console.log('Created post:', result);
} catch (error) {
console.error('Unable to create post:', error.message);
}
};
8.2 Explanation of POST Options
method: 'POST' specifies the HTTP method.
headers contains metadata about the request.
Content-Type: application/json identifies the request body as JSON.
Accept: application/json indicates that the client expects a JSON response.
JSON.stringify(postData) converts the JavaScript object into a JSON string.
response.json() parses the response body when it contains valid JSON.
JSONPlaceholder is a testing API. It may simulate creation of a resource without permanently storing the submitted information.
9. Updating Data with PUT and PATCH
PUT and PATCH are HTTP methods used for updates. PUT commonly replaces a resource, while PATCH commonly modifies selected fields. Always follow the behavior defined by the API documentation.
9.1 PUT Request Example
const updatePost = async () => {
const updatedPost = {
id: 1,
title: 'Updated React Native Notes',
body: 'Updated sample post content.',
userId: 1
};
try {
const response = await fetch(
'https://jsonplaceholder.typicode.com/posts/1',
{
method: 'PUT',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json'
},
body: JSON.stringify(updatedPost)
}
);
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
const data = await response.json();
console.log(data);
} catch (error) {
console.error('Update failed:', error.message);
}
};
9.2 PATCH Request Example
const patchPost = async () => {
try {
const response = await fetch(
'https://jsonplaceholder.typicode.com/posts/1',
{
method: 'PATCH',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
title: 'A New Post Title'
})
}
);
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
const data = await response.json();
console.log(data);
} catch (error) {
console.error('Patch failed:', error.message);
}
};
10. Deleting Data Using Fetch API
A DELETE request asks the API to remove a resource. Depending on the endpoint, the server may return a JSON response or an empty response such as HTTP 204 No Content.
10.1 DELETE Request Example
const deletePost = async (postId) => {
try {
const response = await fetch(
`https://jsonplaceholder.typicode.com/posts/${postId}`,
{
method: 'DELETE'
}
);
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
console.log('Delete request completed.');
} catch (error) {
console.error('Delete failed:', error.message);
}
};
deletePost(1);
10.2 Handling Empty Responses
Do not automatically call response.json() for every request. Parsing an empty response as JSON can fail. Check the API documentation and response status to determine whether the body should be parsed as JSON, read as text, or left unread.
11. Working with Request Headers
HTTP headers communicate additional information about a request. They can identify the request format, specify acceptable response formats, or provide authentication credentials.
11.1 Common Headers
| Header |
Purpose |
| Accept |
Indicates the response formats the client can accept. |
| Content-Type |
Identifies the format of the request body. |
| Authorization |
Provides credentials or a token according to the API's authentication scheme. |
| Cache-Control |
Communicates caching directives when supported. |
11.2 Adding Headers
const response = await fetch(
'https://api.example.com/profile',
{
method: 'GET',
headers: {
Accept: 'application/json',
Authorization: `Bearer ${accessToken}`
}
}
);
accessToken is a placeholder for a valid token obtained through a secure authentication flow. Replace the illustrative endpoint with an actual API endpoint.
11.3 Header Best Practices
- Use the content type required by the backend.
- Send only the headers needed by the API.
- Never log access tokens, passwords, or sensitive personal information.
- Do not hardcode privileged server credentials into the application bundle.
- Use HTTPS for production network communication.
- Follow the backend's authentication and authorization requirements.
12. Passing Query Parameters
Query parameters allow an application to filter, search, sort, or paginate records. They appear after a question mark in the URL, with multiple parameters separated by ampersands.
12.1 Example URL
https://api.example.com/products?category=books&limit=10
12.2 Creating Query Parameters
const params = new URLSearchParams({
category: 'books',
limit: '10',
search: 'react native'
});
const url = `https://api.example.com/products?${params.toString()}`;
const response = await fetch(url);
Query parameters should be encoded correctly so spaces and special characters are handled safely. Verify that the runtime and API support the URL utilities used in the example.
13. Parsing JSON and Other Response Formats
The response body must be read in a format that matches the server's response. JSON is common for REST APIs, but some endpoints return plain text or other formats.
13.1 Parsing JSON
const response = await fetch(
'https://jsonplaceholder.typicode.com/users/1'
);
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
const user = await response.json();
console.log(user.name);
13.2 Reading Plain Text
const response = await fetch('https://api.example.com/status');
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
const text = await response.text();
console.log(text);
13.3 Important Response Properties
| Property or Method |
Description |
response.ok |
True when the HTTP status is between 200 and 299. |
response.status |
The HTTP status code, such as 200, 201, or 404. |
response.headers |
Provides access to response headers. |
response.json() |
Reads and parses the body as JSON. |
response.text() |
Reads the body as text. |
14. Handling Network and API Errors
API requests may fail because of connectivity issues, incorrect endpoints, authentication problems, server errors, or unexpected response formats. Error handling should distinguish between network failures and HTTP error responses where possible.
14.1 Common Error Categories
| Error |
Possible Cause |
Suggested Action |
| Network error |
Connection unavailable, DNS issue, or server unreachable. |
Check connectivity, endpoint, and server availability. |
| 400 Bad Request |
Invalid request data or parameters. |
Validate the request and required fields. |
| 401 Unauthorized |
Missing, expired, or invalid credentials. |
Follow the API's authentication flow. |
| 403 Forbidden |
The caller does not have permission. |
Verify permissions and access rules. |
| 404 Not Found |
The endpoint or resource does not exist. |
Verify the URL and resource ID. |
| 429 Too Many Requests |
Rate limit exceeded. |
Respect rate-limit instructions. |
| 500-level error |
Server-side failure. |
Show a useful message and retry when appropriate. |
| JSON parsing error |
Empty or invalid JSON response. |
Check the response format before parsing. |
14.2 Reusable Error-Handling Pattern
const fetchPosts = async () => {
try {
const response = await fetch(
'https://jsonplaceholder.typicode.com/posts'
);
if (!response.ok) {
throw new Error(
`Request failed with status ${response.status}`
);
}
return await response.json();
} catch (error) {
console.error('API request error:', error.message);
throw error;
}
};
Re-throwing the error allows the calling component to decide how to display the failure. Avoid swallowing errors when the caller needs to know whether the operation succeeded.
15. Preventing Duplicate Requests and Handling Component Lifecycles
React components can render multiple times, and a request may finish after the user has navigated away from a screen. Request cancellation or stale-response protection can help prevent unnecessary work and outdated data from appearing in the interface.
15.1 Using AbortController
Fetch supports cancellation through an AbortSignal in compatible runtime environments. Verify the React Native version and platform behavior for the application being developed.
import React, { useEffect, useState } from 'react';
import { Text, View } from 'react-native';
export default function PostScreen() {
const [post, setPost] = useState(null);
const [error, setError] = useState('');
useEffect(() => {
const controller = new AbortController();
const loadPost = async () => {
try {
const response = await fetch(
'https://jsonplaceholder.typicode.com/posts/1',
{ signal: controller.signal }
);
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
const data = await response.json();
setPost(data);
} catch (err) {
if (err.name !== 'AbortError') {
setError(err.message || 'Unable to load post.');
}
}
};
loadPost();
return () => {
controller.abort();
};
}, []);
if (error) {
return <Text>{error}</Text>;
}
if (!post) {
return <Text>Loading post...</Text>;
}
return (
<View>
<Text>{post.title}</Text>
<Text>{post.body}</Text>
</View>
);
}
15.2 Benefits of Cancellation
- Stops unnecessary client-side network work when the request is no longer needed.
- Helps avoid applying a cancelled request's result to component state.
- Can improve responsiveness when users change screens or search terms quickly.
- Can reduce unnecessary work for obsolete requests.
Cancellation does not guarantee that the server has not already processed a request. For operations that modify data, the backend may complete the operation even if the client stops waiting for its response.
16. Implementing Search with Fetch API
Search interfaces often send requests based on user input. Sending a request for every keystroke can create unnecessary network traffic, so applications should consider debouncing and cancelling outdated requests.
16.1 Search Function Example
const searchUsers = async (searchTerm) => {
const params = new URLSearchParams({
q: searchTerm
});
const response = await fetch(
`https://api.example.com/users?${params.toString()}`
);
if (!response.ok) {
throw new Error(`Search failed: ${response.status}`);
}
return response.json();
};
The endpoint is illustrative. Replace it with a real API that supports the query parameter shown.
16.2 Search Best Practices
- Wait until the user enters a useful search term.
- Debounce input to reduce excessive requests.
- Cancel outdated requests when supported.
- Display loading, empty, and error states.
- Prevent old responses from overwriting results for newer searches.
- Follow the API's rate limits and pagination rules.
17. Pagination and Loading More Records
When an API contains many records, retrieving everything in a single request may increase response time and memory use. Pagination allows an application to load records in smaller groups.
17.1 Page-Based Pagination Example
const fetchPostsPage = async (page, limit = 10) => {
const params = new URLSearchParams({
_page: String(page),
_limit: String(limit)
});
const response = await fetch(
`https://jsonplaceholder.typicode.com/posts?${params.toString()}`
);
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
return response.json();
};
The query parameters in this example are supported by JSONPlaceholder. Other APIs may use page numbers, offsets, cursor tokens, or different parameter names.
17.2 Pagination Checklist
- Track the current page or cursor.
- Append newly retrieved records when loading additional pages.
- Prevent duplicate requests for the same page.
- Detect when no additional records are available.
- Show progress while additional data is loading.
- Handle errors without unnecessarily discarding records already loaded.
18. Using Fetch API with Forms
Forms often submit login details, registration information, contact messages, and feedback to an API. Validate user input before sending it, and ensure that the server also validates incoming data.
18.1 Contact Form Submission Example
const submitContactForm = async (formData) => {
if (!formData.name.trim() || !formData.email.trim()) {
throw new Error('Name and email are required.');
}
const response = await fetch(
'https://api.example.com/contact',
{
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: formData.name.trim(),
email: formData.email.trim(),
message: formData.message.trim()
})
}
);
if (!response.ok) {
throw new Error(`Submission failed: ${response.status}`);
}
return response.json();
};
The endpoint is a placeholder. Replace it with the application's real API and follow its expected response format.
18.2 Form Submission Best Practices
- Validate required fields before sending a request.
- Prevent accidental duplicate submissions while a request is running.
- Display useful success and error messages.
- Avoid logging passwords or sensitive form data.
- Handle server-side validation errors.
- Use HTTPS and follow the backend's authentication requirements.
19. Fetch API and Authentication
Some APIs require authentication before returning private information or accepting modifications. Authentication may use bearer tokens, session cookies, or another mechanism specified by the backend.
19.1 Bearer Token Example
const fetchPrivateProfile = async (accessToken) => {
const response = await fetch(
'https://api.example.com/profile',
{
headers: {
Accept: 'application/json',
Authorization: `Bearer ${accessToken}`
}
}
);
if (!response.ok) {
throw new Error(
`Profile request failed: ${response.status}`
);
}
return response.json();
};
19.2 Authentication Best Practices
- Obtain tokens through the backend's documented authentication process.
- Keep privileged server credentials out of the application bundle.
- Use appropriate secure storage for persistent credentials.
- Handle expired tokens and unauthorized responses according to the backend's requirements.
- Clear authentication data during logout as required.
- Never rely on frontend restrictions as a substitute for backend authorization.
20. Network Security and Platform Considerations
Network requests must be configured correctly for the target platform. React Native networking has some platform-specific behavior, particularly around unencrypted HTTP connections and authentication.
20.1 Prefer HTTPS
Use HTTPS endpoints to protect information while it travels between the application and server. iOS App Transport Security and Android cleartext traffic policies may block unencrypted HTTP requests by default, depending on platform configuration.
20.2 Avoid Broad Security Exceptions
If a development environment requires HTTP, configure only the necessary development exception and follow the platform's guidance. Avoid broadly disabling transport security in production applications.
20.3 Understand Native Networking Behavior
React Native's native networking environment differs from a web browser. Browser Cross-Origin Resource Sharing restrictions do not apply in exactly the same way to native applications, but server access controls, authentication, TLS, and platform networking restrictions still matter.
Reference: React Native Networking Documentation.
21. Organizing API Code for Reusability
As an application grows, putting every network request directly inside UI components can make the code difficult to maintain. A reusable API service can centralize endpoint configuration, response checking, and common request behavior.
21.1 Example API Service
const API_BASE_URL = 'https://jsonplaceholder.typicode.com';
export async function apiGet(path) {
const response = await fetch(`${API_BASE_URL}${path}`, {
headers: {
Accept: 'application/json'
}
});
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
return response.json();
}
21.2 Using the API Service
import { apiGet } from './api';
const loadPosts = async () => {
try {
const posts = await apiGet('/posts');
console.log(posts);
} catch (error) {
console.error('Unable to load posts:', error.message);
}
};
21.3 Advantages of a Reusable API Layer
- Reduces duplicated request logic.
- Centralizes endpoint configuration.
- Encourages consistent response status checking.
- Makes endpoints easier to maintain.
- Makes service functions easier to test independently.
- Allows common headers and request settings to be shared.
For more complex applications, a service layer can be combined with custom hooks, a data-fetching library, or a dedicated state management solution.
22. Testing and Debugging Fetch Requests
Testing API requests helps identify incorrect endpoints, invalid payloads, unexpected responses, and network problems before an application is released.
22.1 Common Debugging Steps
- Verify the API URL and HTTP method.
- Check whether the endpoint is reachable from the target device or emulator.
- Inspect the request headers and body.
- Check the HTTP status code and response body.
- Verify that JSON parsing matches the actual response format.
- Check authentication credentials and permissions without exposing secrets.
- Test loading, empty, success, and error states.
- Test slow connections and interrupted requests.
22.2 Using React Native DevTools
React Native DevTools can record supported network activity, including requests made through Fetch. Network inspection can help developers examine requests and responses during development.
Documentation: React Native DevTools.
22.3 Test Different Scenarios
| Scenario |
Expected Behavior |
| Successful API request |
Parse and display the returned data. |
| Server returns 404 |
Display a useful error message. |
| Device is offline |
Handle the network failure gracefully. |
| Response contains an empty array |
Display an appropriate empty state. |
| Response is not valid JSON |
Handle the parsing failure. |
| Request takes a long time |
Keep the interface responsive and offer an appropriate recovery action. |
| Screen unmounts during a request |
Cancel or safely ignore obsolete work where appropriate. |
23. Common Mistakes When Using Fetch API
| Mistake |
Why It Is a Problem |
Correct Approach |
| Forgetting to await the request |
The code may use a Promise instead of the response data. |
Use await or handle the Promise chain. |
| Forgetting to parse JSON |
The response body has not been converted into a JavaScript object. |
Use response.json() when the response is JSON. |
| Not checking response.ok |
HTTP error statuses do not automatically reject Fetch. |
Check the status before treating the request as successful. |
| Sending an object directly as the body |
JSON endpoints generally expect a serialized request body. |
Use JSON.stringify() and the correct content type. |
| Parsing an empty response as JSON |
Parsing may fail when no JSON body exists. |
Follow the endpoint's response format. |
| Ignoring loading states |
The screen may appear unresponsive. |
Show a progress indicator or loading message. |
| Showing raw technical errors |
Messages may confuse users or expose implementation details. |
Show a friendly message and log safe diagnostic details. |
| Hardcoding secrets |
Application bundles can be inspected. |
Keep privileged secrets on the server. |
| Ignoring outdated requests |
Old responses may overwrite newer data. |
Use cancellation or request identity checks where needed. |
24. Best Practices for Fetch API in React Native
- Use HTTPS for production network communication.
- Use async/await when it improves readability.
- Check
response.ok and HTTP status codes.
- Parse the response according to its actual format.
- Separate API service functions from UI components as the project grows.
- Keep loading, success, empty, and error states clear.
- Use FlatList for long lists rather than rendering large arrays with many nested views.
- Validate input on both the client and server.
- Use cancellation or stale-response protection when necessary.
- Respect API rate limits and avoid unnecessary duplicate requests.
- Never embed privileged secrets in client-side source code.
- Do not log passwords, access tokens, or sensitive personal data.
- Test on the platforms and network conditions the application supports.
- Follow API documentation for authentication, pagination, request formats, and response codes.
25. Mini Project: Build a Users Directory App
Project Objective: Create a React Native screen that retrieves users from an API and displays their names and email addresses. The screen should support loading feedback, error handling, and retry functionality.
25.1 Project Requirements
- Retrieve users from a public testing API.
- Display a loading indicator while the request is running.
- Check the HTTP response status before parsing JSON.
- Store results in React state.
- Render users using FlatList.
- Display a friendly error message if the request fails.
- Display an empty-state message when no records are returned.
- Provide a retry action to reload the data.
25.2 Suggested API Endpoint
https://jsonplaceholder.typicode.com/users
25.3 Suggested Folder Structure
src/
components/
UserCard.js
screens/
UsersScreen.js
services/
api.js
App.js
25.4 Implementation Plan
- Create a React Native screen called
UsersScreen.
- Create state variables for users, loading status, and errors.
- Write an asynchronous function that calls the endpoint using Fetch.
- Check the response status and parse the JSON response.
- Update the users state with the returned data.
- Display a loading indicator while the request is in progress.
- Use FlatList to render each user's name and email address.
- Add a retry button to repeat the request after an error.
- Test successful, empty, offline, and server-error scenarios.
25.5 Expected Result
The application displays a scrollable list of users after successfully retrieving the data. A loading indicator appears while the request is running. If the request fails, an error message and retry option are displayed.
26. Practical Exercises
- Make a GET request to retrieve a list of users and print the result.
- Retrieve a single post using its ID and display its title.
- Create a POST request with a title, body, and user ID.
- Update a sample post using PUT.
- Update only the title of a post using PATCH.
- Send a DELETE request for a sample post.
- Display API data using FlatList.
- Add loading, error, and empty states to an API screen.
- Pass query parameters to filter API results.
- Implement a search function with appropriate request handling.
- Build a paginated list that loads more records when needed.
- Create a reusable API service module.
- Test the application with a disconnected network.
- Cancel an unnecessary request when a screen is unmounted, where supported.
- Build a directory application with retry functionality.
27. Interview Questions and Answers
Q1. What is the Fetch API?
The Fetch API is a JavaScript interface for making network requests and receiving responses from servers. React Native provides Fetch for networking operations.
Q2. Is Fetch built into React Native?
Yes. React Native provides Fetch for basic network requests, so a separate HTTP client library is not required.
Q3. What does fetch() return?
It returns a Promise that resolves to a Response object when a response is received. The body must be read separately.
Q4. How do you parse a JSON response?
Use await response.json() or return response.json() in a Promise chain.
Q5. What is the difference between GET and POST?
GET is commonly used to retrieve information, while POST is commonly used to submit data or create a resource.
Q6. What is async/await?
Async/await is JavaScript syntax for working with Promises in a readable, sequential style.
Q7. Why use try...catch with Fetch?
It allows the application to handle rejected network requests, parsing failures, and errors thrown by application code.
Q8. Does Fetch throw an error for a 404 response?
No. Fetch generally resolves with a Response object for HTTP error statuses. Check response.ok or response.status.
Q9. What does response.ok mean?
It is true when the HTTP status code is between 200 and 299 inclusive.
Q10. Why is JSON.stringify() used in POST requests?
It converts a JavaScript object into a JSON string that can be sent in the request body.
Q11. What is the purpose of the Content-Type header?
It indicates the media type of the request body, such as application/json.
Q12. What is the purpose of the Authorization header?
It carries credentials or tokens according to the API's authentication scheme.
Q13. What is the difference between PUT and PATCH?
PUT commonly replaces a resource, while PATCH commonly updates selected fields. Exact behavior depends on the API.
Q14. What is a DELETE request?
A DELETE request asks the server to remove a specified resource.
Q15. How can you show a loading indicator?
Store a loading flag in state and render an ActivityIndicator while the request is running.
Q16. Why use useEffect for fetching data?
It can initiate a request as a side effect when a component mounts or when its dependencies change.
Q17. What is the purpose of FlatList?
FlatList efficiently renders scrollable lists and is useful for displaying API-driven collections.
Q18. What is an API endpoint?
An endpoint is a URL through which an application accesses a specific API resource or operation.
Q19. What is a network error?
A network error can occur when a request cannot successfully communicate with a server because of connectivity, DNS, TLS, or similar problems.
Q20. How do you handle an empty API response?
Check whether the returned collection contains records and display an appropriate empty-state message when it does not.
Q21. Why check the response status before parsing?
Checking the status allows the application to handle unsuccessful HTTP responses explicitly and avoid treating an error response as valid data.
Q22. What is AbortController?
AbortController provides an AbortSignal that can be used to cancel supported Fetch requests.
Q23. Why should mobile applications use HTTPS?
HTTPS protects data in transit and helps prevent interception or modification of network traffic.
Q24. What is pagination?
Pagination divides a large collection into smaller groups so an application can retrieve the records across multiple requests.
Q25. Why create a reusable API service?
A reusable service reduces duplicated code and centralizes endpoint configuration, response handling, and shared request logic.
Q26. Can Fetch return plain text instead of JSON?
Yes. Use response.text() when the endpoint returns a plain-text response.
Q27. What should happen if a component unmounts during a request?
Where appropriate, cancel the request or ignore obsolete results so they do not cause unnecessary work or overwrite newer state.
Q28. Why should secrets not be hardcoded in a React Native app?
Application bundles can be inspected. Privileged credentials should remain on a secure server rather than being embedded in a client application.
Q29. What is the difference between a Promise rejection and an HTTP error status?
A Promise rejection indicates that the operation failed at the Promise or network level. An HTTP error status is a response received from the server, such as 404 or 500, and must usually be checked explicitly.
Q30. When might another data-fetching library be useful?
A library may help when an application needs caching, request deduplication, background refetching, retries, or more advanced server-state management.
28. Quick Revision Checklist
- Understand APIs, endpoints, and HTTP methods.
- Know the basic syntax of
fetch(url, options).
- Understand Promises and async/await.
- Parse JSON using
response.json().
- Check
response.ok and HTTP status codes.
- Use JSON.stringify() for JSON request bodies.
- Configure headers correctly.
- Handle loading, success, empty, and error states.
- Use React state to display API results.
- Use FlatList to render larger lists.
- Understand GET, POST, PUT, PATCH, and DELETE.
- Use query parameters for filtering and pagination.
- Handle request cancellation and stale responses when necessary.
- Keep credentials secure and use HTTPS.
- Test requests, response parsing, and failure scenarios.
29. Conclusion
The Fetch API is a fundamental tool for building data-driven React Native applications. It enables mobile applications to communicate with backend services, retrieve remote data, submit form information, and perform CRUD operations.
To use Fetch effectively, understand asynchronous programming, configure HTTP methods and headers correctly, parse response bodies appropriately, and check HTTP status codes. Reliable applications also manage loading states, handle failures, protect sensitive credentials, and avoid unnecessary requests.
Practice these techniques by building API-driven screens, forms, search interfaces, and paginated lists. As applications become more complex, organize network logic into reusable service functions and consider a data-fetching library when caching and advanced server-state management are required.
30. Additional Learning Resources